package com.zgzg.shop.pay.base.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.Serializable;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class RedisUtils {

    private static RedisUtils redisUtils;

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 写入缓存
     *
     * @param key   键值
     * @param value 该键值对应的值
     * @return 是否写入成功
     */
    public static boolean set(final String key, Object value) {
        boolean result = false;
        try {
            if (redisUtils.redisTemplate != null) {
                ValueOperations<Serializable, Object> operations = redisUtils.redisTemplate.opsForValue();
                operations.set(key, value);
                result = true;
            }

        } catch (Exception e) {
            log.error("将数据写入缓存出现异常");
        }
        return result;
    }

    /**
     * 写入缓存设置时效时间
     *
     * @param key   键值
     * @param value 该键值对应的值
     * @return 是否写入成功
     */
    public static boolean set(final String key, String value, Long expireTime) {
        boolean result = false;
        try {
            redisUtils.redisTemplate.opsForValue().set(key, value, expireTime, TimeUnit.SECONDS);
            result = true;
        } catch (Exception e) {
            log.error("将数据写入缓存出现异常");
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 设置键的过期时间
     */
    public static boolean expire(String key, long timeout, TimeUnit unit) {
        boolean flag = false;
        try {
            flag = Optional.ofNullable(redisUtils.redisTemplate.expire(key, timeout, unit)).orElse(false);
        } catch (Exception e) {
            log.error("设置key的过期时间失败");
        }
        return flag;
    }

    public static boolean exists(final String key) {
        boolean flag = false;
        try {
            // flag = redisUtils.redisTemplate != null && redisUtils.redisTemplate.hasKey(key);
            flag = Optional.ofNullable(redisUtils.redisTemplate.hasKey(key)).orElse(false);
        } catch (Exception e) {
            log.error("从redis中判断缓存中是否有对应的key出现异常");
        }
        return flag;
    }

    /**
     * 从redis中获取key对应的过期时间;
     * 如果该值有过期时间，就返回相应的过期时间;
     * 如果该值没有设置过期时间，就返回-1;
     * 如果没有该值，就返回-2;
     */
    public static Long getExpire(final String key) {
        return redisUtils.redisTemplate.opsForValue().getOperations().getExpire(key);
    }

    /**
     * 删除对应的value
     */
    public static void remove(final String key) {
        if (exists(key)) {
            redisUtils.redisTemplate.delete(key);
        }
    }

    /**
     * 读取缓存
     */
    public static Object get(final String key) {
        Object result;
        try {
            ValueOperations<Serializable, Object> operations = redisUtils.redisTemplate.opsForValue();
            result = operations.get(key);
        } catch (Exception e) {
            log.error("从redis读取缓存时出现异常");
            return null;
        }
        return result;
    }

    /**
     * 新增一个  sadd
     * 如果集合中之前就已经有数据了，那么返回0，否则返回1
     * @param key
     * @param values
     */
    public static Long sAdd(String key, String... values) {
        Long count;
        try {
            count = redisUtils.redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            log.error("将数据写入缓存出现异常", e);
            return 0L;
        }
        return count;
    }

    /**
     * 查询set数量  SCARD
     * @param key
     * @param values
     */
    public static Long sCount(String key) {
        Long count;
        try {
            count = redisUtils.redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            log.error("查询set集合中元素数量异常", e);
            return 0L;
        }
        return count;
    }

    /**
     * 删除集合中的值  srem
     *
     * @param key
     * @param values
     */
    public static Long sRemove(String key, Object... values) {
        Long count;
        try {
            count = redisUtils.redisTemplate.opsForSet().remove(key, values);
        } catch (Exception e) {
            log.error("删除集合中的值key-values出现异常");
            return null;
        }
        return count;
    }

    /**
     * 判断是否包含  sismember
     *
     * @param key
     * @param value
     */
    public static boolean isMember(String key, Object value) {
        boolean flag = false;
        try {
            flag = redisUtils.redisTemplate.opsForSet().isMember(key, value);
        } catch (Exception e) {
            log.error("从redis中判断是否包含key-value出现异常");
        }
        return flag;
    }

    /**
     * 获取集合中所有的值 smembers
     *
     * @param key
     * @return
     */
    public static Set<Object> values(String key) {
        Set<Object> objects = null;
        try {
            objects = redisUtils.redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            log.error("从redis中获取集合中所有的值出现异常");
        }
        return objects;
    }

    /**
     * 根据前缀删除key
     * @param prefix
     */
    public static void deleteByPrefix(String prefix) {
        prefix = prefix + "**";
        try {
            Set<String> keys = redisUtils.redisTemplate.keys(prefix);
            if (CollUtil.isNotEmpty(keys)) {
                redisUtils.redisTemplate.delete(keys);
            }
        } catch (Exception e) {
            log.error("根据前缀删除key出现异常");
        }
    }

    /**
     * hash  hset
     * @author qy
     * @date 2023/7/4 16:44
     * @param key
     * @param hashKey
     * @param t
     **/
    public static <T> void hSet(String key, String hashKey, T t) {
        try {
            redisUtils.redisTemplate.opsForHash().put(key, hashKey, JSONUtil.toJsonStr(t));
        } catch (Exception e) {
            log.error("redis hset has error", e);
        }
    }

    /**
     * hash  hsetnx
     * @author qy
     * @date 2023/7/4 16:44
     * @param key
     * @param hashKey
     * @param t
     **/
    public static <T> boolean hSetNX(String key, String hashKey, T t) {
        try {
            return redisUtils.redisTemplate.opsForHash().putIfAbsent(key, hashKey, JSONUtil.toJsonStr(t));
        } catch (Exception e) {
            log.error("redis hSetNX has error", e);
        }
        return false;
    }

    /**
     * hash  hDel
     * @author qy
     * @date 2023/7/4 16:44
     * @param key
     * @param hashKey
     **/
    public static Long hDel(String key, String... hashKey) {
        try {
            return redisUtils.redisTemplate.opsForHash().delete(key, hashKey);
        } catch (Exception e) {
            log.error("redis hDel has error", e);
        }
        return 0L;
    }

    /**
     * hash  hGet
     * @author qy
     * @date 2023/7/4 16:44
     * @param key
     * @param hashKey
     **/
    public static <T> T hGet(String key, String hashKey, Class<T> clazz) {
        try {
            Object s =  redisUtils.redisTemplate.opsForHash().get(key, hashKey);
            return JSONUtil.toBean(JSONUtil.toJsonStr(s), clazz);
        } catch (Exception e) {
            log.error("redis hGet has error", e);
        }
        return null;
    }

    /**
     * hash  hExists
     * @author qy
     * @date 2023/7/4 16:44
     * @param key
     * @param hashKey
     **/
    public static boolean hExists(String key, String hashKey) {
        try {
            return redisUtils.redisTemplate.opsForHash().hasKey(key, hashKey);
        } catch (Exception e) {
            log.error("redis hExists has error", e);
        }
        return false;
    }

    /**
     * hash  hGetAll
     * @author qy
     * @date 2023/7/4 16:44
     * @param key
     **/
    public static Map<String, String> hGetAll(String key) {
        try {
            return redisUtils.redisTemplate.opsForHash().entries(key);
        } catch (Exception e) {
            log.error("redis hGetAll has error", e);
        }
        return MapUtil.empty();
    }

    /**
     * hash  hIncrby
     * @author qy
     * @date 2023/7/4 16:44
     * @param key
     * @param hashKey
     **/
    public static Long hIncrby(String key, String hashKey, Integer delta) {
        try {
            return redisUtils.redisTemplate.opsForHash().increment(key, hashKey, delta);
        } catch (Exception e) {
            log.error("redis hIncrby has error", e);
        }
        return 0L;
    }

    @PostConstruct
    public void init() {
        redisUtils = this;
        RedisSerializer stringSerializer = new StringRedisSerializer();
        this.redisTemplate.setKeySerializer(stringSerializer);
        this.redisTemplate.setValueSerializer(stringSerializer);
        this.redisTemplate.setHashKeySerializer(stringSerializer);
        this.redisTemplate.setHashValueSerializer(stringSerializer);
        redisUtils.redisTemplate = this.redisTemplate;
    }

    /**
     * 设置表单字段缓存, 只存储最多5个值, key的有效期为3个月
     * @param key
     * @param value
     */
    public static void addSetValueByKey(String key, String value) {
        // 检查是否已经存在或为空
        if (StringUtils.isBlank(value) || redisUtils.redisTemplate.opsForZSet().rank(key, value) != null) {
            return;
        }
        // 获取当前集合的大小
        long size = redisUtils.redisTemplate.opsForZSet().size(key);

        if (size >= 5) {
            // 如果当前大于等于5个 ,则移除最旧的成员
            Object oldestMember = redisUtils.redisTemplate.opsForZSet().range(key, 0, 0).iterator().next();
            redisUtils.redisTemplate.opsForZSet().remove(key, oldestMember);
        }
        // 添加新成员，并设置当前时间为分数
        redisUtils.redisTemplate.opsForZSet().add(key, value.trim(), System.currentTimeMillis());

        // 设置过期时间为 3 个月
        redisUtils.redisTemplate.expire(key, 90 * 24 * 60 * 60, TimeUnit.SECONDS);
    }

    public static Set<String> getSetValueByKey(String key) {
        // 获取所有成员，并按分数降序排列
        return redisUtils.redisTemplate.opsForZSet().reverseRange(key, 0, -1);
    }


}
